import AvatarView from "@/app/[language]/site/components/AvatarView"; import SiteInput from "@/app/[language]/site/components/SiteInput"; import scss from "@/app/[language]/site/components/SiteYellsView.module.scss"; import SiteYellView from "@/app/[language]/site/components/SiteYellView"; import wsAPI from "@/app/[language]/site/lib/wsAPI"; import { SiteView } from "@/app/[language]/site/state/setSiteStore"; import { OnSiteYellInput } from "@/app/[language]/site/type"; import { useSiteStore } from "@/state/Stores"; import { getDefaultAvatarID } from "@/utilities/Utility"; import { observer } from "mobx-react-lite"; import { useTranslations } from "next-intl"; import { useRouter } from "next/navigation"; import { useEffect, useLayoutEffect, useRef, useState } from "react"; import Alert from "react-bootstrap/Alert"; import ListGroup from "react-bootstrap/ListGroup"; import ListGroupItem from "react-bootstrap/ListGroupItem"; import Offcanvas from "react-bootstrap/Offcanvas"; import OffcanvasBody from "react-bootstrap/OffcanvasBody"; import OffcanvasHeader from "react-bootstrap/OffcanvasHeader"; import Stack from "react-bootstrap/Stack"; import { Item, ItemParams, Menu, useContextMenu } from "react-contexify"; import { toast } from "react-toastify"; import { useWindowArea } from "taehui-lib/fe-utilities"; const EventPB = require("@/Event_pb"); export default observer(({ siteView }: { siteView: SiteView }) => { const { siteID, siteName, siteYellItems, siteNotify, isPendingSiteYellOpened, lastPendingSiteYell, isAvatarsViewOpened, avatars, isSiteHand, setSiteYellsViewLowest, setPendingSiteYellOpened, setAvatarsViewOpened, setSiteYellsView, } = siteView; const { titleView, targetSiteID } = useSiteStore(); const { push } = useRouter(); const t = useTranslations(); const siteYellsView = useRef<HTMLDivElement>(null); const siteInputView = useRef<HTMLDivElement>(null); const [siteYellsViewHeight, setSiteYellsViewHeight] = useState(""); const { windowLength, windowHeight } = useWindowArea(); useLayoutEffect(() => { const tabs = document.getElementById("tabsView"); const title = titleView?.current; const siteInput = siteInputView.current; if (title && tabs && siteInput) { const { height: titleHeight } = title.getBoundingClientRect(); const { height: tabsHeight } = tabs.getBoundingClientRect(); const { height: siteInputHeight } = siteInput.getBoundingClientRect(); setSiteYellsViewHeight( `${windowHeight - 19.5 * 2 - titleHeight - tabsHeight - 2 * 2 - siteInputHeight}px`, ); } }, [titleView, windowLength, windowHeight, targetSiteID]); const { show: viewSiteYellInput } = useContextMenu({ id: "siteYell", }); const { show: viewAvatarInput } = useContextMenu({ id: "avatar", }); const onViewAvatar = ({ props: { avatarID } }: ItemParams) => { if (avatarID.startsWith("*")) { toast.warning(t("notAvatarViewFault")); } else { push(`/avatar/!${getDefaultAvatarID(avatarID)}`); } }; const onSiteYellInput: OnSiteYellInput = (event, avatarID) => { viewSiteYellInput({ event, props: { avatarID } }); }; useEffect(() => { setSiteYellsView(siteYellsView); }, [setSiteYellsView]); useEffect(() => { const { current } = siteYellsView; if (current) { const onSiteYellsViewMove = () => { const { current } = siteYellsView; if (current) { const siteYellID = siteYellItems[0]?.siteYellID; if (siteYellID && siteYellID > 0 && current.scrollTop === 0) { wsAPI.send({ eventID: EventPB.Event.EventID.GET_SITE_YELLS, text: JSON.stringify({ siteID, siteYellID: siteYellID, }), }); } const { height } = current.getBoundingClientRect(); const isSiteYellsViewLowest = Math.ceil(current.scrollTop + height) >= current.scrollHeight; setSiteYellsViewLowest(isSiteYellsViewLowest); if (isSiteYellsViewLowest) { setPendingSiteYellOpened(false); } } }; current.addEventListener("scroll", onSiteYellsViewMove); return () => { current.removeEventListener("scroll", onSiteYellsViewMove); }; } }, [setPendingSiteYellOpened, setSiteYellsViewLowest, siteID, siteYellItems]); return ( <> <Alert show={isPendingSiteYellOpened} className={`m-4 ${scss.alarm}`}> {lastPendingSiteYell && ( <SiteYellView siteYellItem={lastPendingSiteYell} onSiteYellInput={onSiteYellInput} /> )} </Alert> <Alert show={!!siteNotify} className={`m-4 ${scss.alarm}`}> {siteNotify} </Alert> <Stack gap={2}> <div ref={siteYellsView} className={scss.siteView} style={{ height: siteYellsViewHeight }} > <Stack gap={2} className="m-4"> {siteYellItems.map((siteYellItem) => ( <SiteYellView key={siteYellItem.siteYellID} siteYellItem={siteYellItem} onSiteYellInput={onSiteYellInput} /> ))} </Stack> </div> <SiteInput siteView={siteView} ref={siteInputView} /> </Stack> <Menu id="siteYell"> <Item onClick={onViewAvatar}>{t("viewAvatarView")}</Item> </Menu> <Offcanvas placement="end" show={isAvatarsViewOpened} onHide={() => { setAvatarsViewOpened(false); }} > <OffcanvasHeader closeButton> <Offcanvas.Title> {siteName} ({t("avatarCountText", { avatarCount: avatars.length })}) </Offcanvas.Title> </OffcanvasHeader> <OffcanvasBody> <ListGroup> {avatars.map( ({ avatarID, avatarName, avatarConfigure, isSiteHand, isMe, isValve, isAudioInput, }) => ( <ListGroupItem key={avatarID} active={isMe}> <AvatarView avatarID={avatarID} avatarName={avatarName} avatarConfigure={avatarConfigure} isSiteHand={isSiteHand} isValve={isValve} isAudioInput={isAudioInput} onAvatarInput={(event, avatarID) => { viewAvatarInput({ event, props: { avatarID } }); }} /> </ListGroupItem> ), )} </ListGroup> </OffcanvasBody> <Menu id="avatar"> <Item disabled={!isSiteHand} onClick={({ props: { avatarID } }) => { wsAPI.send({ eventID: EventPB.Event.EventID.SET_SITE_OWNER, text: JSON.stringify({ siteID, avatarID, }), }); }} > {t("setSiteHand")} </Item> <Item onClick={onViewAvatar}>{t("viewAvatarView")}</Item> <Item onClick={({ props: { avatarID } }) => { wsAPI.send({ eventID: EventPB.Event.EventID.NEW_SILENT_SITE, text: avatarID, }); }} > {t("silentSiteNew")} </Item> <Item disabled={!isSiteHand} onClick={({ props: { avatarID } }) => { wsAPI.send({ eventID: EventPB.Event.EventID.EXILE_AVATAR, text: JSON.stringify({ siteID, avatarID, }), }); }} > {t("exileAvatar")} </Item> </Menu> </Offcanvas> </> ); });